home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / ASYNCORE.PY < prev    next >
Encoding:
Text File  |  2000-06-02  |  14.7 KB  |  493 lines

  1. # -*- Mode: Python; tab-width: 4 -*-
  2. #   $Id: asyncore.py,v 1.12 2000/06/02 14:22:48 brian Exp $
  3. #   Author: Sam Rushing <rushing@nightmare.com>
  4.  
  5. # ======================================================================
  6. # Copyright 1996 by Sam Rushing
  7. #                         All Rights Reserved
  8. # Permission to use, copy, modify, and distribute this software and
  9. # its documentation for any purpose and without fee is hereby
  10. # granted, provided that the above copyright notice appear in all
  11. # copies and that both that copyright notice and this permission
  12. # notice appear in supporting documentation, and that the name of Sam
  13. # Rushing not be used in advertising or publicity pertaining to
  14. # distribution of the software without specific, written prior
  15. # permission.
  16. # SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  17. # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
  18. # NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  19. # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
  20. # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  21. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  22. # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  23. # ======================================================================
  24.  
  25. import exceptions
  26. import select
  27. import socket
  28. import string
  29. import sys
  30.  
  31. import os
  32. if os.name == 'nt':
  33.     EWOULDBLOCK = 10035
  34.     EINPROGRESS = 10036
  35.     EALREADY    = 10037
  36.     ECONNRESET  = 10054
  37.     ENOTCONN    = 10057
  38.     ESHUTDOWN   = 10058
  39. else:
  40.     from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, ENOTCONN, ESHUTDOWN
  41.  
  42. try:
  43.     socket_map
  44. except NameError:
  45.     socket_map = {}
  46.  
  47. class ExitNow (exceptions.Exception):
  48.     pass
  49.  
  50. DEBUG = 0
  51.  
  52. def poll (timeout=0.0, map=None):
  53.     global DEBUG
  54.     if map is None:
  55.         map = socket_map
  56.     if map:
  57.         r = []; w = []; e = []
  58.         for fd, obj in map.items():
  59.             if obj.readable():
  60.                 r.append (fd)
  61.             if obj.writable():
  62.                 w.append (fd)
  63.         r,w,e = select.select (r,w,e, timeout)
  64.  
  65.         if DEBUG:
  66.             print r,w,e
  67.  
  68.         for fd in r:
  69.             try:
  70.                 obj = map[fd]
  71.                 try:
  72.                     obj.handle_read_event()
  73.                 except ExitNow:
  74.                     raise ExitNow
  75.                 except:
  76.                     obj.handle_error()
  77.             except KeyError:
  78.                 pass
  79.  
  80.         for fd in w:
  81.             try:
  82.                 obj = map[fd]
  83.                 try:
  84.                     obj.handle_write_event()
  85.                 except ExitNow:
  86.                     raise ExitNow
  87.                 except:
  88.                     obj.handle_error()
  89.             except KeyError:
  90.                 pass
  91.  
  92. def poll2 (timeout=0.0, map=None):
  93.     import poll
  94.     if map is None:
  95.         map=socket_map
  96.     # timeout is in milliseconds
  97.     timeout = int(timeout*1000)
  98.     if map:
  99.         l = []
  100.         for fd, obj in map.items():
  101.             flags = 0
  102.             if obj.readable():
  103.                 flags = poll.POLLIN
  104.             if obj.writable():
  105.                 flags = flags | poll.POLLOUT
  106.             if flags:
  107.                 l.append ((fd, flags))
  108.         r = poll.poll (l, timeout)
  109.         for fd, flags in r:
  110.             try:
  111.                 obj = map[fd]
  112.                 try:
  113.                     if (flags  & poll.POLLIN):
  114.                         obj.handle_read_event()
  115.                     if (flags & poll.POLLOUT):
  116.                         obj.handle_write_event()
  117.                 except ExitNow:
  118.                     raise ExitNow
  119.                 except:
  120.                     obj.handle_error()
  121.             except KeyError:
  122.                 pass
  123.  
  124. def loop (timeout=30.0, use_poll=0, map=None):
  125.  
  126.     if use_poll:
  127.         poll_fun = poll2
  128.     else:
  129.         poll_fun = poll
  130.  
  131.         if map is None:
  132.             map=socket_map
  133.  
  134.     while map:
  135.         poll_fun (timeout, map)
  136.  
  137. class dispatcher:
  138.     debug = 0
  139.     connected = 0
  140.     accepting = 0
  141.     closing = 0
  142.     addr = None
  143.  
  144.     def __init__ (self, sock=None, map=None):
  145.         if sock:
  146.             self.set_socket (sock, map)
  147.             # I think it should inherit this anyway
  148.             self.socket.setblocking (0)
  149.             self.connected = 1
  150.  
  151.     def __repr__ (self):
  152.         try:
  153.             status = []
  154.             if self.accepting and self.addr:
  155.                 status.append ('listening')
  156.             elif self.connected:
  157.                 status.append ('connected')
  158.             if self.addr:
  159.                 status.append ('%s:%d' % self.addr)
  160.             return '<%s %s at %x>' % (
  161.                 self.__class__.__name__,
  162.                 string.join (status, ' '),
  163.                 id(self)
  164.                 )
  165.         except:
  166.             try:
  167.                 ar = repr(self.addr)
  168.             except:
  169.                 ar = 'no self.addr!'
  170.                 
  171.             return '<__repr__ (self) failed for object at %x (addr=%s)>' % (id(self),ar)
  172.  
  173.     def add_channel (self, map=None):
  174.         #self.log_info ('adding channel %s' % self)
  175.         if map is None:
  176.             map=socket_map
  177.         map [self._fileno] = self
  178.  
  179.     def del_channel (self, map=None):
  180.         fd = self._fileno
  181.         if map is None:
  182.             map=socket_map
  183.         if map.has_key (fd):
  184.             #self.log_info ('closing channel %d:%s' % (fd, self))
  185.             del map [fd]
  186.  
  187.     def create_socket (self, family, type):
  188.         self.family_and_type = family, type
  189.         self.socket = socket.socket (family, type)
  190.         self.socket.setblocking(0)
  191.         self._fileno = self.socket.fileno()
  192.         self.add_channel()
  193.  
  194.     def set_socket (self, sock, map=None):
  195.         self.__dict__['socket'] = sock
  196.         self._fileno = sock.fileno()
  197.         self.add_channel (map)
  198.  
  199.     def set_reuse_addr (self):
  200.         # try to re-use a server port if possible
  201.         try:
  202.             self.socket.setsockopt (
  203.                 socket.SOL_SOCKET, socket.SO_REUSEADDR,
  204.                 self.socket.getsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR) | 1
  205.                 )
  206.         except:
  207.             pass
  208.  
  209.     # ==================================================
  210.     # predicates for select()
  211.     # these are used as filters for the lists of sockets
  212.     # to pass to select().
  213.     # ==================================================
  214.  
  215.     def readable (self):
  216.         return 1
  217.  
  218.     if os.name == 'mac':
  219.         # The macintosh will select a listening socket for
  220.         # write if you let it.  What might this mean?
  221.         def writable (self):
  222.             return not self.accepting
  223.     else:
  224.         def writable (self):
  225.             return 1
  226.  
  227.     # ==================================================
  228.     # socket object methods.
  229.     # ==================================================
  230.  
  231.     def listen (self, num):
  232.         self.accepting = 1
  233.         if os.name == 'nt' and num > 5:
  234.             num = 1
  235.         return self.socket.listen (num)
  236.  
  237.     def bind (self, addr):
  238.         self.addr = addr
  239.         return self.socket.bind (addr)
  240.  
  241.     def connect (self, address):
  242.         self.connected = 0
  243.         try:
  244.             self.socket.connect (address)
  245.         except socket.error, why:
  246.             if why[0] in (EINPROGRESS, EALREADY, EWOULDBLOCK):
  247.                 return
  248.             else:
  249.                 raise socket.error, why
  250.         self.connected = 1
  251.         self.handle_connect()
  252.  
  253.     def accept (self):
  254.         try:
  255.             conn, addr = self.socket.accept()
  256.             return conn, addr
  257.         except socket.error, why:
  258.             if why[0] == EWOULDBLOCK:
  259.                 pass
  260.             else:
  261.                 raise socket.error, why
  262.  
  263.     def send (self, data):
  264.         try:
  265.             result = self.socket.send (data)
  266.             return result
  267.         except socket.error, why:
  268.             if why[0] == EWOULDBLOCK:
  269.                 return 0
  270.             else:
  271.                 raise socket.error, why
  272.             return 0
  273.  
  274.     def recv (self, buffer_size):
  275.         try:
  276.             data = self.socket.recv (buffer_size)
  277.             if not data:
  278.                 # a closed connection is indicated by signaling
  279.                 # a read condition, and having recv() return 0.
  280.                 self.handle_close()
  281.                 return ''
  282.             else:
  283.                 return data
  284.         except socket.error, why:
  285.             # winsock sometimes throws ENOTCONN
  286.             if why[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]:
  287.                 self.handle_close()
  288.                 return ''
  289.             else:
  290.                 raise socket.error, why
  291.  
  292.     def close (self):
  293.         self.del_channel()
  294.         self.socket.close()
  295.  
  296.     # cheap inheritance, used to pass all other attribute
  297.     # references to the underlying socket object.
  298.     def __getattr__ (self, attr):
  299.         return getattr (self.socket, attr)
  300.  
  301.     # log and log_info maybe overriden to provide more sophisitcated
  302.     # logging and warning methods. In general, log is for 'hit' logging
  303.     # and 'log_info' is for informational, warning and error logging. 
  304.  
  305.     def log (self, message):
  306.         sys.stderr.write ('log: %s\n' % str(message))
  307.  
  308.     def log_info (self, message, type='info'):
  309.         if __debug__ or type != 'info':
  310.             print '%s: %s' % (type, message)
  311.  
  312.     def handle_read_event (self):
  313.         if self.accepting:
  314.             # for an accepting socket, getting a read implies
  315.             # that we are connected
  316.             if not self.connected:
  317.                 self.connected = 1
  318.             self.handle_accept()
  319.         elif not self.connected:
  320.             self.handle_connect()
  321.             self.connected = 1
  322.             self.handle_read()
  323.         else:
  324.             self.handle_read()
  325.  
  326.     def handle_write_event (self):
  327.         # getting a write implies that we are connected
  328.         if not self.connected:
  329.             self.handle_connect()
  330.             self.connected = 1
  331.         self.handle_write()
  332.  
  333.     def handle_expt_event (self):
  334.         self.handle_expt()
  335.  
  336.     def handle_error (self):
  337.         (file,fun,line), t, v, tbinfo = compact_traceback()
  338.  
  339.         # sometimes a user repr method will crash.
  340.         try:
  341.             self_repr = repr (self)
  342.         except:
  343.             self_repr = '<__repr__ (self) failed for object at %0x>' % id(self)
  344.  
  345.         self.log_info (
  346.             'uncaptured python exception, closing channel %s (%s:%s %s)' % (
  347.                 self_repr,
  348.                 t,
  349.                 v,
  350.                 tbinfo
  351.                 ),
  352.             'error'
  353.             )
  354.         self.close()
  355.  
  356.     def handle_expt (self):
  357.         self.log_info ('unhandled exception', 'warning')
  358.  
  359.     def handle_read (self):
  360.         self.log_info ('unhandled read event', 'warning')
  361.  
  362.     def handle_write (self):
  363.         self.log_info ('unhandled write event', 'warning')
  364.  
  365.     def handle_connect (self):
  366.         self.log_info ('unhandled connect event', 'warning')
  367.  
  368.     def handle_accept (self):
  369.         self.log_info ('unhandled accept event', 'warning')
  370.  
  371.     def handle_close (self):
  372.         self.log_info ('unhandled close event', 'warning')
  373.         self.close()
  374.  
  375. # ---------------------------------------------------------------------------
  376. # adds simple buffered output capability, useful for simple clients.
  377. # [for more sophisticated usage use asynchat.async_chat]
  378. # ---------------------------------------------------------------------------
  379.  
  380. class dispatcher_with_send (dispatcher):
  381.     def __init__ (self, sock=None):
  382.         dispatcher.__init__ (self, sock)
  383.         self.out_buffer = ''
  384.  
  385.     def initiate_send (self):
  386.         num_sent = 0
  387.         num_sent = dispatcher.send (self, self.out_buffer[:512])
  388.         self.out_buffer = self.out_buffer[num_sent:]
  389.  
  390.     def handle_write (self):
  391.         self.initiate_send()
  392.  
  393.     def writable (self):
  394.         return (not self.connected) or len(self.out_buffer)
  395.  
  396.     def send (self, data):
  397.         if self.debug:
  398.             self.log_info ('sending %s' % repr(data))
  399.         self.out_buffer = self.out_buffer + data
  400.         self.initiate_send()
  401.  
  402. # ---------------------------------------------------------------------------
  403. # used for debugging.
  404. # ---------------------------------------------------------------------------
  405.  
  406. def compact_traceback ():
  407.     t,v,tb = sys.exc_info()
  408.     tbinfo = []
  409.     while 1:
  410.         tbinfo.append ((
  411.             tb.tb_frame.f_code.co_filename,
  412.             tb.tb_frame.f_code.co_name,             
  413.             str(tb.tb_lineno)
  414.             ))
  415.         tb = tb.tb_next
  416.         if not tb:
  417.             break
  418.  
  419.     # just to be safe
  420.     del tb
  421.  
  422.     file, function, line = tbinfo[-1]
  423.     info = '[' + string.join (
  424.         map (
  425.             lambda x: string.join (x, '|'),
  426.             tbinfo
  427.             ),
  428.         '] ['
  429.         ) + ']'
  430.     return (file, function, line), t, v, info
  431.  
  432. def close_all (map=None):
  433.     if map is None:
  434.         map=socket_map
  435.     for x in map.values():
  436.         x.socket.close()
  437.     map.clear()
  438.  
  439. # Asynchronous File I/O:
  440. #
  441. # After a little research (reading man pages on various unixen, and
  442. # digging through the linux kernel), I've determined that select()
  443. # isn't meant for doing doing asynchronous file i/o.
  444. # Heartening, though - reading linux/mm/filemap.c shows that linux
  445. # supports asynchronous read-ahead.  So _MOST_ of the time, the data
  446. # will be sitting in memory for us already when we go to read it.
  447. #
  448. # What other OS's (besides NT) support async file i/o?  [VMS?]
  449. #
  450. # Regardless, this is useful for pipes, and stdin/stdout...
  451.  
  452. import os
  453. if os.name == 'posix':
  454.     import fcntl
  455.     import FCNTL
  456.  
  457.     class file_wrapper:
  458.         # here we override just enough to make a file
  459.         # look like a socket for the purposes of asyncore.
  460.         def __init__ (self, fd):
  461.             self.fd = fd
  462.  
  463.         def recv (self, *args):
  464.             return apply (os.read, (self.fd,)+args)
  465.  
  466.         def write (self, *args):
  467.             return apply (os.write, (self.fd,)+args)
  468.  
  469.         def close (self):
  470.             return os.close (self.fd)
  471.  
  472.         def fileno (self):
  473.             return self.fd
  474.  
  475.     class file_dispatcher (dispatcher):
  476.         def __init__ (self, fd):
  477.             dispatcher.__init__ (self)
  478.             self.connected = 1
  479.             # set it to non-blocking mode
  480.             flags = fcntl.fcntl (fd, FCNTL.F_GETFL, 0)
  481.             flags = flags | FCNTL.O_NONBLOCK
  482.             fcntl.fcntl (fd, FCNTL.F_SETFL, flags)
  483.             self.set_file (fd)
  484.  
  485.         def set_file (self, fd):
  486.             self._fileno = fd
  487.             self.socket = file_wrapper (fd)
  488.             self.add_channel()
  489.  
  490.